/*
 * client.c
 *
 *  Created on: 11 mei 2013
 *      Author: Pascal
 */

#include "clients.h"

/**
 * Contains a socket and buffer for a client
 */
typedef struct ClientStruct {
	SOCKET clientSocket;
	CircularBuffer circularBuffer;
	int sendLength; // Length of data in the buffer
	HANDLE sendMutex; // Mutex used to regulate access to sendBuffer and sendLength
};

struct ClientStruct clients[MAX_CLIENT_AMOUNT];
char clientSlots[MAX_CLIENT_AMOUNT]; // For keeping track of available client slots

/**
 * Attempts to occupy a client slot. Returns index, TODO: -1 if failed.
 */
int occupyClientSlot(int i) {
	Beep(1000,100);
	// TODO: should probably do something about thread-safety here...
	clientSlots[i] = 1;
	return i;
}

/**
 * Attempts to free a client slot. Returns index, TODO: -1 if failed.
 */
int freeClientSlot(int i) {
	Beep(500,100);
	// TODO: should probably do something about thread-safety here...
	clientSlots[i] = 0;
	return i;
}

/**
 * Attempts to find and reserve an empty client slot. Returns index, -1 if no slot is available.
 */
int findEmptyClientSlot() {
	// TODO: should probably do something about thread-safety here...
	int i;
	for (i = 0; i < MAX_CLIENT_AMOUNT; i++) {
		if (clientSlots[i] == 0) {
			return occupyClientSlot(i);
		}
	}
	return -1;
}

void initClientSlots() {
	int i;
	for (i = 0; i < MAX_CLIENT_AMOUNT; i++) {
		clientSlots[i] = 0;
	}
}

DWORD WINAPI ClientSendThread(int clientStructsIndex) {
	int index = clientStructsIndex;

	int iSendResult;
	while (1) {
		WaitForSingleObject(clients[index].sendMutex,    // handle to mutex
							INFINITE);  // no time-out interval
		while (!cbIsEmpty(&clients[index].circularBuffer)) {
			// There's something to send, so send it

			ElemType elem;
			cbRead(&clients[index].circularBuffer, &elem);
			iSendResult = send(clients[index].clientSocket,
					elem.array, elem.size, 0);

			free(elem.array); // Stop memory from leaking
			elem.size = 0; // Empty the buffer element

			if (iSendResult == SOCKET_ERROR) {
				printf("send failed: %d\n", WSAGetLastError());
				freeClientSlot(index);
				cbFree(&clients[index].circularBuffer);
				ReleaseMutex(clients[index].sendMutex);
				return 0;
			} else {
//				printf("Sent %d bytes to client %d\n", iSendResult, index);
			}
		}
		ReleaseMutex(clients[index].sendMutex);
		Sleep(100);
	}
	return 0;
}

DWORD WINAPI ClientReceiveThread(int clientStructsIndex) {
	int index = clientStructsIndex;
	char receiveBuffer[RECEIVE_BUFFER_SIZE]; // Contains raw received data
	char jsonBuffer[RECEIVE_BUFFER_SIZE]; // Contains received JSON data filtered from receive data
	int jsonBufferIndex = 0;
	struct AnalogAlarmChangeStruct alarmChangeStructA;
	struct BinaryAlarmChangeStruct alarmChangeStructB;
	int openCurlyBraces = 0; // Used to ensure equal number of opening and closing braces
	int closeCurlyBraces = 0; // Used to ensure equal number of opening and closing braces
	int iResult;
	int i;

	while (1) {
		// While the connection is valid, wait for data, and print the buffer
		iResult = recv(clients[index].clientSocket, receiveBuffer,
				RECEIVE_BUFFER_SIZE, 0);
		if (iResult > 0) {
			printf("Bytes received: %d\n", iResult);
			receiveBuffer[iResult] = '\0';

			puts("receiveBuffer: ");
			puts(receiveBuffer);
			fflush(stdout);

			puts("\nFiltering result:");
			// Look for first opening curly brace
			// This seems to do sufficient filtering already
			for (i = 0; i < iResult; i++) {
				if (receiveBuffer[i] == '{') {
					break;
				}
			}
			for (; i < iResult; i++) {
				jsonBuffer[jsonBufferIndex] = receiveBuffer[i];
				jsonBufferIndex++;
				if (receiveBuffer[i] == '{') {
					openCurlyBraces++;
				} else if (receiveBuffer[i] == '}') {
					closeCurlyBraces++;
				}

				// Check if the final closing brace has been reached
				// It's possible objects are spread across receives
				if (openCurlyBraces > 0) {
					if (openCurlyBraces == closeCurlyBraces) {
						// Full object received, close it with a null char
						jsonBuffer[jsonBufferIndex] = '\0';

						int parseReturn = parseAlarmChangeJson(
								jsonBuffer,
								&alarmChangeStructA,
								&alarmChangeStructB);
						if (parseReturn == 0) {
							// Parsing was unsuccessful, ignore
						} else if (parseReturn == 1) {
							puts("Changing analog alarm");
							// Parsed analog change
							findAndChangeAnalogAlarm(&alarmChangeStructA);
						} else if (parseReturn == 2) {
							puts("Changing binary alarm");
							// Parsed binary change
							findAndChangeAnalogAlarm(&alarmChangeStructB);
						}
						fflush(stdout);

						openCurlyBraces = 0;
						closeCurlyBraces = 0;
						jsonBufferIndex = 0;
					}
				}
			}
		} else if (iResult == 0) {
			printf("Connection closing...\n");
			freeClientSlot(index);
			return 0;
		} else {
			printf("Receive failed: %d\n", WSAGetLastError());
			closesocket(clients[index].clientSocket);
			freeClientSlot(index);
			return 0;
		}
	}
	return 0;
}

/**
 * Puts data into the send buffer of every client
 */
void putInSendBuffers(char data[], int length) {
	int i;
	for (i = 0; i < MAX_CLIENT_AMOUNT; i++) {
		// Should check if there is still a transmission in progress (check sendLength?)
		if (clientSlots[i]) {
			WaitForSingleObject(clients[i].sendMutex,    // handle to mutex
					INFINITE);  // no time-out interval

			if (cbIsFull(&clients[i].circularBuffer)) {
				puts("CIRCULAR BUFFER FULL - IGNORING MESSAGE!!!");
			} else {
				ElemType elem;
				elem.array = malloc(length);
				elem.size = length;
				memcpy(elem.array, data, length);
				cbWrite(&clients[i].circularBuffer, &elem);
			}

			ReleaseMutex(clients[i].sendMutex);
		}
	}
}

/**
 * Waits for clients, then creates a thread to listen to that client.
 */
DWORD WINAPI CommThread(void* data) {
	initSocket();
	while (1) {
		SOCKET clientSocket = waitForConnection();
		if (initNewClient(clientSocket) == -1) {
			puts(
					"Couldn't initialize client, maximum of clients already reached.\n");
			Sleep(100);
		}
	}
	return 0;
}

/**
 * Initializes a client struct and starts the send/receive threads.
 */
int initNewClient(SOCKET clientSocket) {
	int clientIndex = findEmptyClientSlot();
	if (clientIndex == -1) {
		return -1;
	}
	puts("Initializing client struct.");
	fflush(stdout);
	clients[clientIndex].clientSocket = clientSocket;
	clients[clientIndex].sendLength = 0;
	cbInit(&clients[clientIndex].circularBuffer, CIRCULAR_BUFFER_SIZE);
	clients[clientIndex].sendMutex = CreateMutex(NULL, // default security attributes
			FALSE,             // initially not owned
			NULL );             // unnamed mutex

	puts("Creating client send & receive threads.");
	fflush(stdout);
	CreateThread(NULL, 0, ClientReceiveThread, clientIndex, 0, NULL );
	CreateThread(NULL, 0, ClientSendThread, clientIndex, 0, NULL );
}
